有位畫室主人,開店前想得很遠
他心想:「以後客人可能會要油畫框、水彩框、粉彩框……」於是一口氣訂了十種畫框,擺滿整面牆
半年後盤點,九種畫框,一次都沒被用過,畫室目前,只接油畫委託
那九種畫框,不是「準備好了、還沒用到」,是佔用了牆面、綁住了一筆錢
只為了一個從未發生的「以後」
Day 15 之後,訂單報表功能持續在長大
開發者想到「以後可能要支援匯出成 CSV、Excel、甚至 PDF」,於是先把架構「做好」:
public abstract class OrderExportGenerator
{
protected readonly IReadOnlyList<Order> Orders;
protected OrderExportGenerator(IReadOnlyList<Order> orders)
{
Orders = orders;
}
public abstract void Export(string path, string reservedForFutureLocale);
}
目前系統裡,唯一的子類別實作:
public class CsvOrderExportGenerator : OrderExportGenerator
{
public CsvOrderExportGenerator(IReadOnlyList<Order> orders) : base(orders) { }
public override void Export(string path, string reservedForFutureLocale)
{
// ...寫出 CSV 的邏輯...
}
}
呼叫端長這樣:
public class OrderExportService
{
public void ExportMonthlyOrders(IReadOnlyList<Order> orders)
{
OrderExportGenerator generator = new CsvOrderExportGenerator(orders);
generator.Export("monthly-orders.csv", null);
}
}
打開這段程式碼,會看到三個「準備好但沒派上用場」的訊號:
OrderExportGenerator,只有一個子類別 CsvOrderExportGenerator
reservedForFutureLocale,從建立以來,一直只被傳入 null
OrderExportGenerator generator,但實際上只可能被指派成 CsvOrderExportGenerator,多型帶來的彈性,在這裡是虛的
這跟今天開頭那十個畫框是同一種處境,它們看起來像是「未雨綢繆」,實際上只是把牆面(程式碼的複雜度)先佔住,換來一個可能永遠不會兌現的方便
更諷刺的是:如果半年後真的要支援 Excel 匯出,工程師打開這個抽象類別,大概率會發現
「當初猜的介面設計,跟 Excel 匯出真正需要的介面,對不上」得先拆掉這層猜測性的架構,才能開始做真正的需求
解法是摺疊繼承體系 (Collapse Hierarchy):把子類別的實作,直接併回一個具體類別,砍掉那層沒有實際作用的抽象
public class CsvOrderExportGenerator
{
private readonly IReadOnlyList<Order> _orders;
public CsvOrderExportGenerator(IReadOnlyList<Order> orders)
{
_orders = orders;
}
public void Export(string path)
{
// ...寫出 CSV 的邏輯...
}
}
沒用過的參數 reservedForFutureLocale,用移除參數 (Remove Parameter) 直接拿掉
呼叫端也跟著變得誠實:
public class OrderExportService
{
public void ExportMonthlyOrders(IReadOnlyList<Order> orders)
{
var generator = new CsvOrderExportGenerator(orders);
generator.Export("monthly-orders.csv");
}
}
少了一個抽象類別、少了一個永遠是 null 的參數,程式碼變得更簡單,卻沒有損失任何目前真正用得到的能力
答案不是「永遠不要抽象」,是把抽象的時機,留到真正出現第二種需求的那一刻
到那時候,你手上有兩份真實的實作可以比較(CSV 跟 Excel),能提煉出真正符合兩者需求的介面
這比賽前猜測的抽象,準確得多,也省得下一次重構
這正是 YAGNI 原則(You Aren't Gonna Need It)的核心:
不是不做設計,是不要為了不確定的未來,在當下先付出複雜度的代價
模組四到這裡,六種贅肉都盤點完了
明天我們走進畫室的日常運作,看看師傅跟學徒之間,該怎麼分工才不會互相踩線
模組五:畫室的分工倫理(The Couplers),正式開工